
import java.io.File;
import java.io.FileFilter;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;

/**
 * ProducerConsumer
 * <p/>
 * Producer and consumer tasks in a desktop search application
 *
 * @author Brian Goetz and Tim Peierls
 */
public class ProducerConsumer {
    static class FileCrawler implements Runnable {
        private final List<BlockingQueue<File>> fileQueue;
        private final FileFilter fileFilter;
        private final File root;
       // private final CountDownLatch endCrawler;
        private int counter;
        private final ExecutorService executorForCrawler;

        public FileCrawler(List<BlockingQueue<File>> fileQueue,
                           final FileFilter fileFilter,
                           File root,
                           int counter,
                           ExecutorService executorForCrawler) {
            this.fileQueue = fileQueue;
            this.root = root;
            this.counter = counter;
            this.executorForCrawler = executorForCrawler;
            this.fileFilter = new FileFilter() {
                public boolean accept(File f) {
                    return f.isDirectory() ||  fileFilter.accept(f);
                }
            };
        }

        private boolean alreadyIndexed(File f) {
            return false;
        }

        public void run() {
            try {
                crawl(root);
                counter--;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        private void crawl(File root) throws InterruptedException {
            File[] entries = root.listFiles(fileFilter);
            int i = 0;
            if (entries != null) {
                for (File entry : entries)
                    if (entry.isDirectory()) {
                    	counter++;
                        executorForCrawler.submit(new FileCrawler(fileQueue, fileFilter, entry, counter, executorForCrawler));
                    }
                    else if (!alreadyIndexed(entry)) {
                    	fileQueue.get(i).put(entry);
                    	i++;
                    	if (i == fileQueue.size())
                    		i = 0;
                    }
            }
        }
    }

    static class Indexer implements Runnable {
        private final List<BlockingQueue<File>> listQueue;
        private final int numberOfQueue;

        public Indexer(List<BlockingQueue<File>> listQueue, int i) {
            this.listQueue = listQueue;
            this.numberOfQueue = i;
        }

        public void run() {
        	int i = numberOfQueue;
            try {
                while (true) {
                	if (!(listQueue.get(numberOfQueue).isEmpty())&&(i != numberOfQueue))
        				i = numberOfQueue;
                	else
                		if (listQueue.get(i).isEmpty()) {
                				i++;
                				if (i == listQueue.size())
                					i = 0;
                		}
                	indexFile(listQueue.get(i).take());
                }
            } catch (InterruptedException e) {
            	if (listQueue.get(numberOfQueue).isEmpty()) {
            		Thread.currentThread().interrupt();
            	}
            	else {
            		while (!(listQueue.get(numberOfQueue).isEmpty())) {
            			try {
							indexFile(listQueue.get(numberOfQueue).take());
						} catch (InterruptedException e1) {
							Thread.currentThread().interrupt();
						}
            		}
            	}
            }
        }

        public void indexFile(File file) {
            // Index the file...
        	System.out.println(file.getName());
        };
    }

    private static final int BOUND = 10;
    private static final int N_CONSUMERS = Runtime.getRuntime().availableProcessors();

    public static void startIndexing(File[] roots) throws InterruptedException {
    	List<BlockingQueue<File>> listOfQueues = new LinkedList<BlockingQueue<File>>();
    	//final CountDownLatch endDownForCrawler = new CountDownLatch(roots.length);
    	//Thread[] mas = new Thread[N_CONSUMERS];
    	int counter = roots.length;
    	ExecutorService executorForCrawler = Executors.newFixedThreadPool(roots.length);
    	ExecutorService executorForIndexer = Executors.newFixedThreadPool(N_CONSUMERS);
        //BlockingQueue<File> queue = new LinkedBlockingQueue<File>(BOUND);
    	for (int i = 0; i < N_CONSUMERS; i++) {
    		listOfQueues.add(new LinkedBlockingQueue<File>(BOUND));
    	}
        FileFilter filter = new FileFilter() {
            public boolean accept(File file) {
                return true;
            }
        };
        for (File root : roots) {
        	executorForCrawler.submit(new FileCrawler(listOfQueues, filter, root, counter, executorForCrawler));
        }
        /*for (File root : roots) {
            new Thread(new FileCrawler(listOfQueues, filter, root, endDownForCrawler)).start();
        }

        for (int i = 0; i < N_CONSUMERS; i++) {
        	mas[i] = new Thread(new Indexer(listOfQueues, i));
        	mas[i].start();
        }*/
        //endDownForCrawler.await();
        while (counter != 0)
        	Thread.sleep(500);
        executorForCrawler.shutdown();
        System.out.println("test");
        for (int i = 0; i < N_CONSUMERS; i++) {
        	executorForIndexer.submit(new Indexer(listOfQueues, i));
        }
        executorForIndexer.shutdown();
    }
}
